package cn.wolfcode.service.impl;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import cn.wolfcode.common.exception.BusinessException;
import cn.wolfcode.common.web.CommonCodeMsg;
import cn.wolfcode.common.web.Result;
import cn.wolfcode.domain.*;
import cn.wolfcode.mapper.OrderInfoMapper;
import cn.wolfcode.mapper.PayLogMapper;
import cn.wolfcode.mapper.RefundLogMapper;
import cn.wolfcode.mq.OrderMessage;
import cn.wolfcode.redis.SeckillRedisKey;
import cn.wolfcode.service.IOrderInfoService;
import cn.wolfcode.service.ISeckillProductService;
import cn.wolfcode.util.IdGenerateUtil;
import cn.wolfcode.web.feign.IntegralFeignApi;
import cn.wolfcode.web.feign.PayFeignApi;
import cn.wolfcode.web.msg.SeckillCodeMsg;
import io.seata.spring.annotation.GlobalTransactional;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;

/**
 * Created by wolfcode-lanxw
 */
@Service
public class OrderInfoSeviceImpl implements IOrderInfoService {
    @Autowired(required = false)
    private ISeckillProductService seckillProductService;
    @Autowired(required = false)
    private OrderInfoMapper orderInfoMapper;
    @Autowired(required = false)
    private StringRedisTemplate redisTemplate;
    @Autowired(required = false)
    private PayLogMapper payLogMapper;
    @Autowired(required = false)
    private RefundLogMapper refundLogMapper;

    @Autowired
    private RedissonClient redissionClient;

    @Value("${pay.returnUrl}")
    private String returnUrl;

    @Value("${pay.notifyUrl}")
    private String notifyUrl;

    @Resource
    private PayFeignApi payFeignApi;

    @Value("${pay.frontEndPayUrl}")
    private String redirectUrl;

    @Resource
    private IntegralFeignApi integralFeignApi;


    @Override
    @GlobalTransactional
    public String order(OrderMessage orderMessage) {
        // 表操作逻辑
        RLock lock = redissionClient.getLock("order_lock");
        lock.lock(30, TimeUnit.SECONDS);
        OrderInfo orderInfo =
                orderInfoMapper.findByPhoneAndSeckillId(orderMessage.getUserPhone(),
                        orderMessage.getSeckillId());
        if (!StringUtils.isEmpty(orderInfo)) {
            throw new BusinessException(SeckillCodeMsg.REPEAT_SECKILL);
        }

        // 2 判断真实库存是否为 零
        SeckillProductVo vo = seckillProductService.queryVoFromDB(orderMessage.getSeckillId());

        if (StringUtils.isEmpty(vo) || vo.getStockCount() <= 0) {
            throw new BusinessException(SeckillCodeMsg.SECKILL_ERROR);
        }
        // 1 插入一个订单信息
        OrderInfo info = new OrderInfo();
        String orderNo = String.valueOf(IdGenerateUtil.get().nextId());
        info.setOrderNo(orderNo);
        info.setUserId(orderMessage.getUserPhone());
        info.setProductId(vo.getProductId());
//        info.setDeliveryAddrId(0L);
        info.setProductName(vo.getProductName());
        info.setProductImg(vo.getProductImg());
        info.setProductCount(1);       //秒杀商品只允许买一个
        info.setProductPrice(vo.getProductPrice());
        info.setSeckillPrice(vo.getSeckillPrice());
        info.setIntergral(vo.getIntergral());
        info.setStatus(OrderInfo.STATUS_ARREARAGE);
        info.setCreateDate(new Date());
//        info.setPayDate(new Date());
//        info.setPayType(0);
        info.setSeckillDate(new Date());
        info.setSeckillTime(orderMessage.getTime());
        info.setSeckillId(orderMessage.getSeckillId());

        int m = orderInfoMapper.insert(info);   //操作成功的行数
        if (m < 1) {
            throw new BusinessException(SeckillCodeMsg.SECKILL_STOCK_OVER);
        }

        // 2 表真实库存减一
        m = seckillProductService.decrStock(orderMessage.getSeckillId());
        if (m < 1) {
            throw new BusinessException(SeckillCodeMsg.SECKILL_STOCK_OVER);
        }

        return orderNo;
    }

    @Override
    public OrderInfo getOrderById(String orderNo) {
        return orderInfoMapper.find(orderNo);
    }

    @Override
    public int changeTimeOut(String orderNo) {
        return orderInfoMapper.updateCancelStatus(orderNo, OrderInfo.STATUS_TIMEOUT);
    }

    @Override
    public OrderInfo find(String orderNo) {
        return orderInfoMapper.find(orderNo);
    }

    @Override
    public String aplipay(String orderNo, Integer type) {
        //1 根据订单标号获取订单信息
        OrderInfo orderInfo = orderInfoMapper.find(orderNo);
        //2 根据type ，决定是用支付宝还是积分
        if (type == OrderInfo.PAYTYPE_ONLINE) {
            return this.alipayOnline(orderInfo);
        }
        return this.alipayOnIntegral(orderInfo);
    }

    @Override
    @Transactional
    public String notifyUrl(Map<String, String> params) {
        //0 验签
        Result<Boolean> result = payFeignApi.rsaCheckV1(params);
        if (StringUtils.isEmpty(result) || result.hasError()) {
            return "fail";
        }
        if (!result.getData()) {
            return "fail";
        }
        //1  插入支付日志
        PayLog payLog = new PayLog();
        payLog.setTradeNo(params.get("trade_no"));
        payLog.setOutTradeNo(params.get("out_trade_no"));
        payLog.setNotifyTime(new Date().toString());
        payLog.setTotalAmount(params.get("total_amount"));
        payLog.setPayType(OrderInfo.PAYTYPE_ONLINE);

        int m = payLogMapper.insert(payLog);
        if (m < 1) {
            return "fail";
        }
        //修改订单状态为已支付

        int i = orderInfoMapper.changePayStatus(params.get("out_trade_no"), OrderInfo.STATUS_ACCOUNT_PAID,
                OrderInfo.PAYTYPE_ONLINE);
        if (i < 1) {
            return "fail";
        }
        return "success";

    }

    @Override
    public void returnUrl(Map<String, String> params, HttpServletResponse response) {
        //1 验签
        Result<Boolean> result = payFeignApi.rsaCheckV1(params);
        if (StringUtils.isEmpty(result) || result.hasError()) {
            throw new BusinessException(SeckillCodeMsg.PAY_SERVER_ERROR);
        }
        if (!result.getData()) {
            throw new BusinessException(SeckillCodeMsg.PAY_SERVER_ERROR);
        }
        //2 通过notify判断是否已经执行完成
        PayLog payLog = payLogMapper.query(params.get("out_trade_no"));
        if (StringUtils.isEmpty(payLog)) {
            throw new BusinessException(SeckillCodeMsg.PAY_SERVER_ERROR);
        }
        //3  跳转到订单详情页面
        try {
            response.sendRedirect(redirectUrl + params.get("out_trade_no"));
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    @GlobalTransactional   //指定TM
    public String refund(String orderNo) {
        //1 查询订单信息
        OrderInfo orderInfo = orderInfoMapper.find(orderNo);
        //2 根据类型，进行付款
        if (orderInfo.getPayType() == OrderInfo.PAYTYPE_ONLINE) {
            return this.refundOnline(orderInfo);
        }
        return this.refundOnIntegral(orderInfo);
    }


    private String refundOnIntegral(OrderInfo orderInfo) {
        //1调用积分服务进行操作
        OperateIntergralVo vo = new OperateIntergralVo();
        vo.setPk(orderInfo.getOrderNo());
        vo.setValue(orderInfo.getIntergral());
        vo.setInfo(orderInfo.getProductName());
        vo.setUserId(orderInfo.getUserId());

        Result<Boolean> result = integralFeignApi.refundOnIntegral(vo);
        if (StringUtils.isEmpty(result) || result.hasError()) {
            throw new BusinessException(CommonCodeMsg.RESULT_IS_EMPTY);
        }
        if (StringUtils.isEmpty(result.getData()) || !result.getData()) {
            throw new BusinessException(SeckillCodeMsg.REFUND_ERROR);
        }

        //2 插入退款日志
        //2  如果退款成功， 添加退款日志
        RefundLog refundLog = new RefundLog();
        refundLog.setOutTradeNo(orderInfo.getOrderNo());
        refundLog.setRefundAmount(orderInfo.getIntergral().toString());
        refundLog.setRefundReason("穷，懒");
        refundLog.setRefundType(OrderInfo.PAYTYPE_INTERGRAL);
        refundLog.setRefundTime(new Date());
        System.err.println("***********************************************");
        int m = refundLogMapper.insert(refundLog);
        if (m < 1) {
            throw new BusinessException(SeckillCodeMsg.REFUND_ERROR);

        }
        //3  修改订单状态
        m = orderInfoMapper.changeRefundStatus(orderInfo.getOrderNo(), OrderInfo.STATUS_REFUND);
        if (m < 1) {
            throw new BusinessException(SeckillCodeMsg.REFUND_ERROR);

        }

        return "积分退款成功";


    }

    private String refundOnline(OrderInfo orderInfo) {
        //1  调用支付服务，  进行退款操作
        //1 验签
        RefundVo refundVo = new RefundVo();
        refundVo.setOutTradeNo(orderInfo.getOrderNo());
        refundVo.setRefundAmount(orderInfo.getSeckillPrice().toString());
        refundVo.setRefundReason("饿了");
        Result<Boolean> result = payFeignApi.refundOnLine(refundVo);
        if (StringUtils.isEmpty(result) || result.hasError()) {
            throw new BusinessException(SeckillCodeMsg.PAY_SERVER_ERROR);
        }
        if (!result.getData()) {
            throw new BusinessException(SeckillCodeMsg.PAY_SERVER_ERROR);
        }
        //2  如果退款成功， 添加退款日志
        RefundLog refundLog = new RefundLog();
        refundLog.setOutTradeNo(orderInfo.getOrderNo());
        refundLog.setRefundAmount(orderInfo.getSeckillPrice().toString());
        refundLog.setRefundReason(refundVo.getRefundReason());
        refundLog.setRefundType(OrderInfo.PAYTYPE_ONLINE);
        refundLog.setRefundTime(new Date());

        int m = refundLogMapper.insert(refundLog);
        if (m < 1) {
            throw new BusinessException(SeckillCodeMsg.REFUND_ERROR);

        }
        //3  修改订单状态
        orderInfoMapper.changeRefundStatus(orderInfo.getOrderNo(), OrderInfo.STATUS_REFUND);
        return "支付宝退款成功";

    }

    private String alipayOnIntegral(OrderInfo orderInfo) {
        //1调用积分服务进行操作
        OperateIntergralVo vo = new OperateIntergralVo();
        vo.setPk(orderInfo.getOrderNo());
        vo.setValue(orderInfo.getIntergral());
        vo.setInfo(orderInfo.getProductName());
        vo.setUserId(orderInfo.getUserId());

        Result<Boolean> result = integralFeignApi.alipayOnIntergral(vo);
        if (StringUtils.isEmpty(result) || result.hasError()) {
            throw new BusinessException(CommonCodeMsg.RESULT_IS_EMPTY);
        }
        if (StringUtils.isEmpty(result.getData()) || !result.getData()) {
            throw new BusinessException(SeckillCodeMsg.INTERGRAL_SERVER_ERROR);
        }

        //2 如果付款成功，插入支付日志
        PayLog payLog = new PayLog();
        payLog.setTradeNo(orderInfo.getOrderNo());
        payLog.setOutTradeNo(orderInfo.getOrderNo());
        payLog.setNotifyTime(new Date().toString());
        payLog.setTotalAmount(orderInfo.getIntergral().toString());
        payLog.setPayType(OrderInfo.PAYTYPE_INTERGRAL);

        int m = payLogMapper.insert(payLog);
        if (m < 1) {
            throw new BusinessException(SeckillCodeMsg.INTERGRAL_SERVER_ERROR);
        }
        //3 修改订单状态为已支付
        m = orderInfoMapper.changePayStatus(orderInfo.getOrderNo(),
                OrderInfo.STATUS_ACCOUNT_PAID, OrderInfo.PAYTYPE_INTERGRAL);
        if (m < 1) {
            throw new BusinessException(SeckillCodeMsg.INTERGRAL_SERVER_ERROR);

        }
        //修改淡定状态为已支付
//        System.out.println(1/0);

        return "积分支付成功";
    }


    private String alipayOnline(OrderInfo orderInfo) {
        //1 调用支付服务进行支付
        // (4个表单数据+notify+return)
        PayVo payVo = new PayVo();
        payVo.setOutTradeNo(orderInfo.getOrderNo());
        payVo.setTotalAmount(orderInfo.getSeckillPrice().toString());
        payVo.setSubject(orderInfo.getProductName());
        payVo.setBody(orderInfo.getProductName());
        payVo.setReturnUrl(returnUrl);
        payVo.setNotifyUrl(notifyUrl);

        Result<String> result = payFeignApi.alipayOnline(payVo);
        if (StringUtils.isEmpty(result) || result.hasError()) {
            throw new BusinessException(SeckillCodeMsg.PAY_SERVER_ERROR);
        }
        //表单+js
        return result.getData();
    }

}
